/*
 *  Copyright 2007 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.service.core.impl;

import com.blandware.atleap.common.util.PartialCollection;
import com.blandware.atleap.common.util.QueryInfo;
import com.blandware.atleap.model.core.*;
import com.blandware.atleap.persistence.core.*;
import com.blandware.atleap.service.core.SequenceManager;
import com.blandware.atleap.service.exception.BeanAlreadyExistsException;
import com.blandware.atleap.service.exception.BeanNotFoundException;
import com.blandware.atleap.service.exception.OwnerNotFoundException;

/**
 * <p>Implementation of SequenceManager.</p>
 * <p><a href="SequenceManagerImpl.java.html"><i>View Source</i></a>
 * </p>
 *
 * @author Roman Puchkovskiy <a href="mailto:roman.puchkovskiy@blandware.com">
 *         &lt;roman.puchkovskiy@blandware.com&gt;</a>
 * @version : $Revision: 1.3 $ : 2007/12/17 13:38:25 $
 */
public class SequenceManagerImpl extends BaseManagerImpl
        implements SequenceManager {

    /**
     * Localizable DAO
     */
	protected LocalizableDAO localizableDAO;
    /**
     * Sequence DAO
     */
	protected SequenceDAO sequenceDAO;
    /**
     * Sequence item DAO
     */
	protected SequenceItemDAO sequenceItemDAO;
    /**
     * Page DAO
     */
    protected PageDAO pageDAO;
    /**
     * Content resource DAO
     */
    protected ContentResourceDAO contentResourceDAO;

    /**
     * Sets DAO for operating with localizables.
     *
     * @param localizableDAO the DAO to set
     */
	public void setLocalizableDAO(LocalizableDAO localizableDAO) {
		this.localizableDAO = localizableDAO;
	}

    /**
     * Sets DAO for operating with sequences.
     *
     * @param sequenceDAO the DAO to set
     */
	public void setSequenceDAO(SequenceDAO sequenceDAO) {
		this.sequenceDAO = sequenceDAO;
	}

    /**
     * Sets DAO for operating with sequence items.
     *
     * @param sequenceItemDAO the DAO to set
     */
	public void setSequenceItemDAO(SequenceItemDAO sequenceItemDAO) {
		this.sequenceItemDAO = sequenceItemDAO;
	}

    /**
     * Sets DAO for operating with pages.
     *
     * @param pageDAO the DAO to set
     */
    public void setPageDAO(PageDAO pageDAO) {
        this.pageDAO = pageDAO;
    }

    /**
     * Sets DAO for operating with content resources.
     *
     * @param contentResourceDAO the DAO to set
     */
    public void setContentResourceDAO(ContentResourceDAO contentResourceDAO) {
        this.contentResourceDAO = contentResourceDAO;
    }

    //  S E Q U E N C E S

	//~ CRUD Methods ================================================================

	/**
	 * @see com.blandware.atleap.service.core.SequenceManager#createSequence(com.blandware.atleap.model.core.Sequence, Long) 
	 */
	public Long createSequence(Sequence sequence, Long ownerId)
            throws BeanNotFoundException, BeanAlreadyExistsException {

		if ( log.isDebugEnabled() ) {
			log.debug("Creating new sequence...");
		}

		Localizable localizable;
		localizable = localizableDAO.retrieveLocalizable(ownerId);

		if ( localizable == null ) {
			String errorMessage = "No localizable with ID=" + ownerId + "could be found";
			if ( log.isErrorEnabled() ) {
				log.error(errorMessage);
			}
			throw new OwnerNotFoundException(errorMessage);
		}

		if ( sequenceDAO.hasDuplicates(sequence, ownerId) ) {
			throw new BeanAlreadyExistsException("Sequence with the same identifier already exists on owner with ID=" + ownerId);
		}

		Long sequenceId = sequenceDAO.createSequence(sequence, localizable);

		if ( log.isDebugEnabled() ) {
			log.debug("Sequence has been created successfully. Its ID is " + sequenceId);
		}
		return sequenceId;
	}

	/**
	 * @see com.blandware.atleap.service.core.SequenceManager#retrieveSequence(Long)
	 */
	public Sequence retrieveSequence(Long sequenceId) {
		Sequence sequence;
		sequence = sequenceDAO.retrieveSequence(sequenceId);
		return sequence;
	}

	/**
	 * @see com.blandware.atleap.service.core.SequenceManager#updateSequence(com.blandware.atleap.model.core.Sequence, Long) 
	 */
	public void updateSequence(Sequence sequence, Long localizableId)
            throws BeanNotFoundException, BeanAlreadyExistsException {

		// remove field from cache in order to prevent Hibernate from assigning new version number
		sequenceDAO.removeFromCache(sequence);

		if ( log.isDebugEnabled() ) {
			log.debug("Updating sequence with ID=" + sequence.getId() + "...");
		}

		Localizable localizable;
		localizable = localizableDAO.retrieveLocalizable(localizableId);

		if ( localizable == null ) {
			String errorMessage = "No localizable with ID=" + localizableId + "could be found";
			if ( log.isErrorEnabled() ) {
				log.error(errorMessage);
			}
			throw new OwnerNotFoundException(errorMessage);
		}

		if ( sequenceDAO.hasDuplicates(sequence, localizableId) ) {
			throw new BeanAlreadyExistsException("Sequence with the same identifier already exists on owner with ID=" + localizableId);
		}

		// update sequence
		sequenceDAO.updateSequence(sequence, localizable);

		if ( log.isDebugEnabled() ) {
			log.debug("Sequence was updated successfully.");
		}
	}

	/**
	 * @see com.blandware.atleap.service.core.SequenceManager#deleteSequence(Long) 
	 */
	public void deleteSequence(Long sequenceId) throws BeanNotFoundException {
		if ( log.isDebugEnabled() ) {
			log.debug("Deleting sequence with ID=" + sequenceId);
		}

		Sequence sequence = sequenceDAO.retrieveSequence(sequenceId);
		if ( sequence == null ) {
			String errorMessage = "No sequence with ID=" + sequenceId + "could be found";
			throw new BeanNotFoundException(errorMessage);
		}

		sequenceDAO.deleteSequence(sequence);

		if ( log.isDebugEnabled() ) {
			log.debug("Sequence has been deleted successfully");
		}
	}

	// ~ Additional methods ================================================================

	/**
	 * @see com.blandware.atleap.service.core.SequenceManager#listSequences(com.blandware.atleap.common.util.QueryInfo) 
	 */
	public PartialCollection listSequences(QueryInfo queryInfo) {
		return sequenceDAO.listSequences(queryInfo);
	}

	// ~ Finders ================================================================

	/**
	 * @see com.blandware.atleap.service.core.SequenceManager#findSequenceByLayoutDefinition(String, String)
	 */
	public Sequence findSequenceByLayoutDefinition(String identifier, String definition) {
		return sequenceDAO.findSequenceByLayoutDefinition(identifier, definition);
	}

	/**
	 * @see com.blandware.atleap.service.core.SequenceManager#findSequenceByPageUri(String, String)
	 */
	public Sequence findSequenceByPageUri(String identifier, String uri) {
		return sequenceDAO.findSequenceByPageUri(identifier, uri);
	}

    

    //  S E Q U E N C E   I T E M S

	//~ CRUD Methods ================================================================

	/**
	 * @see com.blandware.atleap.service.core.SequenceManager#createPageSequenceItem(com.blandware.atleap.model.core.SequenceItem, Long, Long) 
	 */
	public Long createPageSequenceItem(SequenceItem sequenceItem,
            Long sequenceId, Long pageId) throws BeanNotFoundException {
        return createSequenceItem(sequenceItem, sequenceId, pageId, null);
	}

    /**
	 * @see com.blandware.atleap.service.core.SequenceManager#createContentResourceSequenceItem(com.blandware.atleap.model.core.SequenceItem, Long, Long)  
	 */
	public Long createContentResourceSequenceItem(SequenceItem sequenceItem,
            Long sequenceId, Long contentResourceId) throws BeanNotFoundException {
        return createSequenceItem(sequenceItem, sequenceId, null, contentResourceId);
	}

    /**
	 * @see com.blandware.atleap.service.core.SequenceManager#retrieveSequenceItem(Long) 
	 */
	public SequenceItem retrieveSequenceItem(Long sequenceItemId) {
		SequenceItem sequenceItem;
		sequenceItem = sequenceItemDAO.retrieveSequenceItem(sequenceItemId);
		return sequenceItem;
	}

	/**
	 * @see com.blandware.atleap.service.core.SequenceManager#updatePageSequenceItem(com.blandware.atleap.model.core.SequenceItem, Long, Long) 
	 */
	public void updatePageSequenceItem(SequenceItem sequenceItem,
            Long sequenceId, Long pageId) throws BeanNotFoundException {
        updateSequenceItem(sequenceItem, sequenceId, pageId, null);
    }

	/**
	 * @see com.blandware.atleap.service.core.SequenceManager#updateContentResourceSequenceItem(com.blandware.atleap.model.core.SequenceItem, Long, Long)  
	 */
	public void updateContentResourceSequenceItem(SequenceItem sequenceItem,
            Long sequenceId, Long contentResourceId) throws BeanNotFoundException {
        updateSequenceItem(sequenceItem, sequenceId, null, contentResourceId);
    }

    /**
	 * @see com.blandware.atleap.service.core.SequenceManager#deleteSequenceItem(Long) 
	 */
	public void deleteSequenceItem(Long sequenceItemId) throws BeanNotFoundException {
		if ( log.isDebugEnabled() ) {
			log.debug("Deleting sequence item with ID=" + sequenceItemId);
		}

		SequenceItem sequenceItem = sequenceItemDAO.retrieveSequenceItem(sequenceItemId);
		if ( sequenceItem == null ) {
			String errorMessage = "No sequence item with ID=" + sequenceItemId + "could be found";
			throw new BeanNotFoundException(errorMessage);
		}

		sequenceItemDAO.deleteSequenceItem(sequenceItem);
		if ( log.isDebugEnabled() ) {
			log.debug("Sequence item has been deleted successfully");
		}
	}

	// ~ Additional methods ================================================================

	/**
	 * @see com.blandware.atleap.service.core.SequenceManager#listSequenceItems(com.blandware.atleap.common.util.QueryInfo) 
	 */
	public PartialCollection listSequenceItems(QueryInfo queryInfo) {
		return sequenceItemDAO.listSequenceItems(queryInfo);
	}

    /**
     * @see com.blandware.atleap.service.core.SequenceManager#moveSequenceItem(com.blandware.atleap.model.core.SequenceItem, boolean)
     */
    public void moveSequenceItem(SequenceItem sequenceItem, boolean up) {
        sequenceItemDAO.moveSequenceItem(sequenceItem, up);
    }

    /**
     * @see com.blandware.atleap.service.core.SequenceManager#getSequenceItemFirstLastOccupiedPositions(Long) 
     */
    public Integer[] getSequenceItemFirstLastOccupiedPositions(Long ownerId) {
        return sequenceItemDAO.getFirstLastOccupiedPositions(ownerId);
    }

    // ~ Finders ================================================================

    // ~ Private methods ================================================================

    /**
     * Creates a sequence item - pointing either to page or to content resource.
     * Exactly one of pageId and contentResourceId must not be null.
     *
     * @param sequenceItem      item to be created
     * @param sequenceId        ID of  sequence to which item will belong
     * @param pageId            ID of page to which item will point
     * @param contentResourceId ID of content resource to which item will point
     * @return ID of created sequence item
     * @throws BeanNotFoundException if page or content resource with given ID
     * cannot be found
     */
    private Long createSequenceItem(SequenceItem sequenceItem, Long sequenceId,
            Long pageId, Long contentResourceId) throws BeanNotFoundException {

        if ( log.isDebugEnabled() ) {
            log.debug("Creating new sequence item...");
        }

        Sequence sequence = sequenceDAO.retrieveSequence(sequenceId);
        if ( sequence == null ) {
            String errorMessage = "No sequence with ID=" + sequenceId + "could be found";
            if ( log.isErrorEnabled() ) {
                log.error(errorMessage);
            }
            throw new OwnerNotFoundException(errorMessage);
        }

        Long sequenceItemId;
        if (pageId != null) {
            Page page = pageDAO.retrievePage(pageId);
            if ( page == null ) {
                String errorMessage = "No page with identifier=" + pageId + "could be found";
                if ( log.isErrorEnabled() ) {
                    log.error(errorMessage);
                }
                throw new BeanNotFoundException(errorMessage);
            }
            sequenceItemId = sequenceItemDAO.createSequenceItem(sequenceItem, sequence, page);
        } else {
            ContentResource contentResource = contentResourceDAO.retrieveContentResource(contentResourceId);
            if ( contentResource == null ) {
                String errorMessage = "No content resource with identifier=" + contentResourceId + "could be found";
                if ( log.isErrorEnabled() ) {
                    log.error(errorMessage);
                }
                throw new BeanNotFoundException(errorMessage);
            }
            sequenceItemId = sequenceItemDAO.createSequenceItem(sequenceItem, sequence, contentResource);
        }

        if ( log.isDebugEnabled() ) {
            log.debug("Sequence item has been created successfully. Its ID is " + sequenceItemId);
        }
        return sequenceItemId;
    }

    /**
     * Updates a sequence item - pointing either to page or to content resource.
     * Exactly one of pageId and contentResourceId must not be null.
     *
     * @param sequenceItem      item to be created
     * @param sequenceId        ID of  sequence to which item will belong
     * @param pageId            ID of page to which item will point
     * @param contentResourceId ID of content resource to which item will point
     * @throws com.blandware.atleap.service.exception.BeanNotFoundException if page or content resource with given ID
     * cannot be found
     */
    private void updateSequenceItem(SequenceItem sequenceItem, Long sequenceId,
            Long pageId, Long contentResourceId) throws BeanNotFoundException {
         // remove value from cache in order to prevent Hibernate from assigning new version number
		sequenceItemDAO.removeFromCache(sequenceItem);

		if ( log.isDebugEnabled() ) {
			log.debug("Updating sequence item with ID=" + sequenceItem.getId() + "...");
		}

		Sequence sequence = sequenceDAO.retrieveSequence(sequenceId);
		if ( sequence == null ) {
			String errorMessage = "No sequence with ID=" + sequenceId + "could be found";
			if ( log.isErrorEnabled() ) {
				log.error(errorMessage);
			}
			throw new OwnerNotFoundException(errorMessage);
		}

        if (pageId != null) {
            Page page = pageDAO.retrievePage(pageId);
            if ( page == null ) {
                String errorMessage = "No page with identifier=" + pageId + "could be found";
                if ( log.isErrorEnabled() ) {
                    log.error(errorMessage);
                }
                throw new BeanNotFoundException(errorMessage);
            }
            // updating sequence item
            sequenceItemDAO.updateSequenceItem(sequenceItem, sequence, page);
        } else {
            ContentResource contentResource = contentResourceDAO.retrieveContentResource(contentResourceId);
            if ( contentResource == null ) {
                String errorMessage = "No content resource with identifier=" + contentResourceId + "could be found";
                if ( log.isErrorEnabled() ) {
                    log.error(errorMessage);
                }
                throw new BeanNotFoundException(errorMessage);
            }
            // updating sequence item
            sequenceItemDAO.updateSequenceItem(sequenceItem, sequence, contentResource);
        }

        if ( log.isDebugEnabled() ) {
			log.debug("Sequence item was updated successfully.");
		}
    }    
}